﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace KnowledgeKiosk
{
    /// <summary>
    /// Defines all application configurable settings
    /// </summary>
    [DataContract]
    public class Settings
    {
        private static Settings _default;

        private Settings()
        {
        }

        /// <summary>
        /// Gets or sets the Uri of the Info Support intraportal SharePoint Lists Web Service.
        /// </summary>
        [DataMember(Order = 0)]
        [Description("Uri of the Info Support intraportal SharePoint Lists Web Service.")]
        [DefaultValue(typeof(Uri), @"http://intraportal/kc/_vti_bin/Lists.asmx")]
        public Uri SharePointListServiceUri { get; set; }

        /// <summary>
        /// Gets or sets the ID of the Info Support SharePoint List that contains the Course data.
        /// </summary>
        [DataMember(Order = 1)]
        [Description("ID of the Info Support SharePoint List that contains the Course data.")]
        [DefaultValue(typeof(Guid), "{9076E475-32EA-46BA-9A6D-D997E45B237E}")]
        public Guid SharePointListID { get; set; }

        /// <summary>
        /// Gets or sets the update interval of the Info Support SharePoint Updater.
        /// </summary>
        [DataMember(Order = 2)]
        [Description("Update interval of the Info Support SharePoint Updater.")]
        [DefaultValue(typeof(TimeSpan), "00:01:00")]
        public TimeSpan SharePointUpdateInterval { get; set; }

        /// <summary>
        /// Gets or sets the Uri of the blogs RSS feed.
        /// </summary>
        [DataMember(Order = 3)]
        [Description("Uri of the blogs RSS feed.")]
        [DefaultValue(typeof(Uri), @"http://blogs.infosupport.com/feed/")]
        public Uri BlogsFeedUri { get; set; }

        /// <summary>
        /// Gets or sets the update interval of the blogs RSS feed.
        /// </summary>
        [DataMember(Order = 4)]
        [Description("Update interval of the blogs RSS feed.")]
        [DefaultValue(typeof(TimeSpan), "00:15:00")]
        public TimeSpan BlogsFeedUpdateInterval { get; set; }

        /// <summary>
        /// Gets or sets the maximum number of blog posts the feed viewer should display.
        /// </summary>
        [DataMember(Order = 5)]
        [Description("Maximum number of blog posts the feed viewer should display.")]
        [DefaultValue(5)]
        public int BlogsFeedItemLimit { get; set; }

        /// <summary>
        /// Gets or sets the WiFi SSID displayed on the information screen's WiFi widget.
        /// </summary>
        [DataMember(Order = 6)]
        [Description("WiFi SSID displayed on the information screen's WiFi widget.")]
        [DefaultValue("WLANFREE")]
        public string WiFiSSID { get; set; }

        /// <summary>
        /// Gets or sets the WiFi key displayed on the information screen's WiFi widget.
        /// </summary>
        [DataMember(Order = 7)]
        [Description("WiFi key displayed on the information screen's WiFi widget.")]
        [DefaultValue("kenniscentrum")]
        public string WiFiKey { get; set; }

        /// <summary>
        /// Gets or sets the Uri of the weather API.
        /// </summary>
        [DataMember(Order = 8)]
        [Description("Uri of the weather API.")]
        [DefaultValue(typeof(Uri), @"http://openweathermap.org/data/2.1/find/name?q=veenendaal")]
        public Uri WeatherFeedUri { get; set; }

        /// <summary>
        /// Gets or sets the update interval of the weather feed.
        /// </summary>
        [DataMember(Order = 9)]
        [Description("Update interval of the weather feed.")]
        [DefaultValue(typeof(TimeSpan), "00:05:00")]
        public TimeSpan WeatherFeedUpdateInterval { get; set; }

        /// <summary>
        /// Gets or sets a value that indicates whether logging is enabled.
        /// </summary>
        [DataMember(Order = 10)]
        [Description("Enables logging.")]
        [DefaultValue(false)]
        public bool LogEnabled { get; set; }

        /// <summary>
        /// Gets or sets the name of the theme.
        /// </summary>
        [DataMember(Order = 11)]
        [Description("Name of the theme.")]
        [DefaultValue("Rotate")]
        public string Theme { get; set; }

        /// <summary>
        /// Gets the default settings.
        /// </summary>
        public static Settings Default
        {
            get
            {
                if (_default == null)
                {
                    _default = new Settings();
                    _default.InitializeDefaultValues();
                }
                return _default;
            }
        }

        /// <summary>
        /// Loads the default values for all settings
        /// </summary>
        internal void InitializeDefaultValues()
        {
            var result = this.GetType().GetProperties()
                .Where(pi => pi.GetCustomAttributes(typeof(DataMemberAttribute), false).Any())
                .Where(pi => pi.GetCustomAttributes(typeof(DefaultValueAttribute), false).Cast<DefaultValueAttribute>().Any(dv => dv.Value != null))
                .Select(pi => new
                {
                    Property = pi,
                    DefaultValue = pi.GetCustomAttributes(typeof(DefaultValueAttribute), false).Cast<DefaultValueAttribute>().Single().Value
                });

            foreach (var item in result)
            {
                item.Property.SetValue(this, item.DefaultValue, null);
            }
        }

        /// <summary>
        /// Loads the default values before deserializing the external configuration file
        /// </summary>
        /// <param name="context"></param>
        [OnDeserializing]
        internal void OnDeserializing(StreamingContext context)
        {
            InitializeDefaultValues();
        }

        /// <summary>
        /// Reads and deserializes settings from a configuration XML file
        /// </summary>
        /// <param name="path">Path of the XML file</param>
        /// <returns>The deserialized settings</returns>
        public static Settings Load(string path)
        {
            var s = new DataContractSerializer(typeof (Settings));
            Settings settings;
            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                settings = s.ReadObject(fs) as Settings;
            }
            return settings;
        }

        /// <summary>
        /// Writes all values of the settings object to a configuration XML file
        /// </summary>
        /// <param name="path">Path of the XML file</param>
        public void Save(string path)
        {
            var s = new DataContractSerializer(typeof (Settings));
            using (var fs = new FileStream(path, FileMode.CreateNew, FileAccess.Write))
            {
                s.WriteObject(fs, this);
            }
        }

        private void WriteDescription()
        {
            var result = this.GetType().GetProperties()
                .Where(pi => pi.GetCustomAttributes(typeof(DataMemberAttribute), false).Any())
                .Select(pi => new
                {
                    Name = pi.Name, 
                    Description = pi.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>().SingleOrDefault().With(a=>a.Description),
                    DefaultValue = pi.GetCustomAttributes(typeof(DefaultValueAttribute), false).Cast<DefaultValueAttribute>().SingleOrDefault().With(a => a.Value)
                });
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<!--");
            foreach (var item in result)
            {
                sb.AppendLine(item.Name);
                if(!String.IsNullOrEmpty(item.Description))
                  sb.AppendFormat("  Description: {0}\r\n", item.Description);
                if (item.DefaultValue != null)
                    sb.AppendFormat("  Default Value: {0}\r\n", item.DefaultValue);
                sb.AppendLine();
            }
            sb.AppendLine("-->");
            var description = sb.ToString();
        }
    }

    sealed class DefaultValueAttribute : System.ComponentModel.DefaultValueAttribute
    {
        public DefaultValueAttribute(object value) : base(value)
        {}

        public DefaultValueAttribute(Type type, string value)
            : base(type, value)
        {
            if (Value == null && !String.IsNullOrEmpty(value))
            {
                if (type == typeof(Uri))
                    SetValue(new Uri(value));
                else if (type == typeof(Guid))
                    SetValue(Guid.Parse(value));
            }
        }
    }
}